import got, { type Request } from "got";
import { type BrowserWindow } from "electron";
import { createWriteStream } from "fs";
import path from "node:path";
import fs from "node:fs";
import { EventEmitter } from "events";
import type { DownloadCompleteArgs, DownloadErrorArgs } from "@common/http-types.ts";
import { IS_DEV } from "@elec/main/util/env.ts";
import { throttle } from "radash";

export interface _DownloadProgressArgs {
  transferred: number;
  total: number;
  percent: number;
}

/**
 * http下载任务
 */
export class DownloadTask extends EventEmitter {
  static readonly UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36";

  private _ds?: Request;
  private _speed = 0;
  private _speedSamples: { time: number; bytes: number }[] = [];

  private _progressArgs: any = null;

  private readonly _sendProgressToRendererThrottled: () => void;
  private _complete = false;

  constructor(
    readonly id: string,
    readonly url: string,
    readonly savePath: string,
    readonly wind: BrowserWindow | undefined,
  ) {
    super();

    this._sendProgressToRendererThrottled = throttle({ interval: 1000 }, () => !this._complete && this._sendProgressToRenderer());
  }

  private get webContents() {
    return this.wind?.webContents;
  }

  start() {
    const saveDir = path.dirname(this.savePath);
    if (!fs.existsSync(saveDir)) {
      fs.mkdirSync(saveDir, { recursive: true });
    }
    const outStream = createWriteStream(this.savePath);
    if (IS_DEV) console.log(`[download-task] start download: ${this.url} => ${this.savePath}`);
    const stream = got.stream(this.url, {
      headers: {
        "User-Agent": DownloadTask.UA,
        Referer: new URL(this.url).origin + "/",
      },
      followRedirect: true,
      https: {
        rejectUnauthorized: false,
      },
    });
    stream.on("downloadProgress", this._onProgress);
    stream.on("error", this._onError);
    stream.on("end", this._onEnd);
    stream.pipe(outStream);
    this._ds = stream;
  }

  private _onProgress = (args: _DownloadProgressArgs) => {
    if (args.total > 0) {
      // 计算速度
      const now = Date.now();
      // 2秒窗口
      const windowSizeMs = 2000;

      // 添加新样本
      this._speedSamples.push({
        time: now,
        bytes: args.transferred,
      });

      // 移除过期样本
      while (this._speedSamples[0]?.time < now - windowSizeMs) {
        this._speedSamples.shift();
      }

      // 至少两个样本才计算
      if (this._speedSamples.length >= 2) {
        const first = this._speedSamples[0];
        const last = this._speedSamples[this._speedSamples.length - 1];

        const timeDiff = (last.time - first.time) / 1000;
        if (timeDiff > 0) {
          this._speed = (last.bytes - first.bytes) / timeDiff;
        }
      }

      this._progressArgs = { ...args, speed: this._speed };
      this._sendProgressToRendererThrottled();
    }
  };

  private _sendProgressToRenderer() {
    if (this._progressArgs) {
      this.webContents?.send(`file.download.progress.${this.id}`, this._progressArgs);
    }
  }

  private _onError = (err: Error) => {
    if (IS_DEV) console.error(`[download-task] download error: ${this.url}`, err);
    this.webContents?.send(`file.download.error.${this.id}`, <DownloadErrorArgs>{
      message: err.message,
    });
    this.emit("error", err);
    this._deleteSaveFile();
  };

  private _onEnd = () => {
    this._complete = true;
    this._sendProgressToRenderer();

    if (IS_DEV) console.log(`[download-task] download complete: ${this.savePath}`);
    this.webContents?.send(`file.download.complete.${this.id}`, { filePath: this.savePath } as DownloadCompleteArgs);
    this.emit("complete");
  };

  private _deleteSaveFile() {
    if (fs.existsSync(this.savePath)) {
      fs.rmSync(this.savePath, { force: true, maxRetries: 3, retryDelay: 300 });
    }
  }

  cancel() {
    if (this._ds) {
      this._ds.destroy();
      setTimeout(() => {
        this._deleteSaveFile();
      }, 200);
      this._ds = undefined;
    }
  }

  pause() {
    this._ds?.pause();
  }

  resume() {
    this._ds?.resume();
  }

  private static readonly _tasks = new Map<string, DownloadTask>();

  /**
   * 提交一个下载任务
   * @param id 任务ID
   * @param url 下载地址
   * @param savePath 保存路径
   * @param wind 窗口对象
   */
  static submit(id: string, url: string, savePath: string, wind?: BrowserWindow) {
    if (this._tasks.has(id)) {
      return;
    }

    const task = new DownloadTask(id, url, savePath, wind);
    this._tasks.set(id, task);
    task.on("error", () => this._tasks.delete(id));
    task.on("complete", () => this._tasks.delete(id));
    task.start();
    return task;
  }

  /**
   * 取消下载任务
   * @param id 任务ID
   */
  static cancel(id: string) {
    this._tasks.get(id)?.cancel();
    this._tasks.delete(id);
  }
}
